package org.NooLab.randomgraph.storage;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
 
import org.NooLab.itexx.storage.ConnectorClientIntf;
import org.NooLab.itexx.storage.DataBaseCreateCommand;
import org.NooLab.itexx.storage.DatabaseMgmt;
import org.NooLab.itexx.storage.DbConnector;
import org.NooLab.itexx.storage.DbLogin;
import org.NooLab.randomgraph.RandomGraphIntf;
import org.NooLab.randomgraph.properties.PersistenceSettings;
import org.NooLab.randomgraph.properties.RGraphProperties;
import org.NooLab.randomgraph.storage.db.h2.DataBaseHandler;
import org.NooLab.randomgraph.storage.db.h2.DataBaseHandlerIntf;
import org.NooLab.randomgraph.storage.db.h2.MetaData;
import org.NooLab.randomgraph.storage.db.iciql.Wordfinger;
import org.NooLab.randomgraph.util.XmlStringHandling;
import org.NooLab.structures.randomgraph.RGraphPropertiesDefaultIntf;
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.objects.StringedObjects;
import org.NooLab.utilities.resources.ResourceLoader;
import org.NooLab.utilities.strings.ArrUtilities;
import org.apache.commons.dbutils.QueryRunner;
import org.h2.jdbc.JdbcSQLException;

import com.iciql.Db;

import org.h2.server.Service;
import org.h2.tools.Server;

/**
 * 
 * SELECT sum(length) FROM WORDFINGER where RAWLABEL like 'pre%';
   SELECT count(*) FROM WORDFINGER;

 *
 */
public class RGraphDataBase implements ConnectorClientIntf {

	String cfgResourceJarPath = "org/NooLab/randomgraph/resources/sql/" ; // trailing / needed !!
	
	RGraphPropertiesDefaultIntf rgProperties ;
	private boolean dbFileExists;
	private boolean isOpen;
	private boolean dbRecreated = false;
	
	
	String databaseName = "";
	String configFile = "";
	String storageDir = "" ; 
	int _DB_TARGET_LOCATING=0;
	
	String databaseUrl="";

	String accessMode = "tcp"; // "http" = external server via 8082, "file" 
	Server server;
	Connection connection;
	Db iciDb;
	DatabaseMetaData jdbMetaData ;
	String dbCatalog ;
	MetaData metaData ; 
	
	DataBaseHandlerIntf dbHandler ;
	
	String user = "";
	String password = "" ;
	
	Class parent;
	String databaseFile="";
	
	DFutils fileutil = new DFutils();
	PrintLog out = new PrintLog(2,false);

	private PersistenceSettings ps;
	StringedObjects strObj = new StringedObjects();
	
	// ========================================================================
	public RGraphDataBase( RGraphPropertiesDefaultIntf rgProperties ){
		this.rgProperties = rgProperties ;
		
		ps = (PersistenceSettings) rgProperties.getPersistenceSettings();
		
		// sth like "rg-fingerprints";
		databaseName =  ps.getDatabaseName();
		user = ps.getDbUser() ;
		password = ps.getDbpassword() ;
		String configFile = databaseName+".h2.db-config.xml";
		
		
		getStorageDir();
		
		out.setPrefix("[RGraph-DB]");
	}

	public RGraphDataBase(){

		out.setPrefix("[rgraph-db]");
	}

	// ========================================================================
	
	public boolean dbFileExists(){
		return dbFileExists;
	}
	
	public boolean isOpen(){
		return isOpen;
	}
	
	
	private boolean removeLock(){
		boolean rB=false;
		
		try{
		

			if (connection==null){
				ArrayList<String> locks = fileutil.listOfFiles("lock",".db", storageDir);
				for (int i=0;i<locks.size();i++){
					String fname = locks.get(i);
					if (DFutils.fileExists( fname)){
						fileutil.deleteFile( fname) ;
					}
				}
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return rB;
	}

	public boolean prepareDatabase() throws Exception {
		boolean rB=false;
		
		 
		try {
			// remove any lock 
			if (connection==null){
				removeLock();
			}else{
				return true;
			}
			
			int servermodeFlag=0;
			if (accessMode.contentEquals("tcp")){
				// if in server mode
				String h2Dir = DatabaseMgmt.setH2BaseDir(storageDir, _DB_TARGET_LOCATING); // DatabaseMgmt._BASEDIR_QUERY_STOR) ;
				h2Dir = DFutils.createPath(h2Dir, "storage/") ;
				createServer( databaseName,h2Dir) ;
				servermodeFlag=1;
			}else{
				if (accessMode.contentEquals("http")){
					servermodeFlag=1;
				}
			}
			//  
			String h2Dir = DatabaseMgmt.setH2BaseDir(storageDir, _DB_TARGET_LOCATING); // DatabaseMgmt._BASEDIR_QUERY_STOR) ;
			h2Dir = DFutils.createPath(h2Dir, "storage/") ;

			databaseFile = connect( databaseName, h2Dir, servermodeFlag ) ;
			
			
			
			rB = databaseFile.length()>0;
			
			if (rB){
				
				dbHandler = new DataBaseHandler( this ) ;
				
				if ((checkStructure(connection)==false) || (dbRecreated) ){ // 
					createDatabaseStructure( databaseName ) ;// "rg-fingerprints"
				}else{
					metaData = dbHandler.getMetadata();
					metaData.retrieveMetaData();
					
					String str = ArrUtilities.arr2Text( metaData.getTableNames(1),";");

					out.printErr(3, "Tables : "+str) ;
				}
				
			}
			
			if ((connection!=null) && (connection.isClosed()==false)){
				connection.close() ;
			}
			
		} catch (FileNotFoundException e) {
			rB=false;
			e.printStackTrace();
		}
		 
		return rB;
	}

	//             e.g. resourceName = "create-db-sql-xml" ;
	private String getConfigResource( String resourceName ) throws Exception{
		
		String resourcePath = cfgResourceJarPath + resourceName;
		
		String xmlstr = "" ;
		boolean rB;
		
		ResourceLoader rsrcLoader = new ResourceLoader();   
		rB = rsrcLoader.loadTextResource( this.getClass(), resourcePath  ) ;
		if (rB){  
			xmlstr = rsrcLoader.getTextResource();
			
		}else{
			throw(new Exception("unable to load resources (create-db-sql-xml)")) ;
		}
		
		return xmlstr ;
	}

	
	private void createDatabaseStructure(String dbname) throws Exception {
		// loading the  "create-db-sql-xml"  
		// from package "org.NooLab.docserver.resources.sql"
		boolean rB;
		String xmlstr = "" ;
		
		xmlstr = getConfigResource("create-db-sql-xml");
		
		connect( databaseName,storageDir) ;
		
		// get the create statements
		ArrayList<DataBaseCreateCommand> ccs = dbHandler.getCreateFromResource( xmlstr, dbname ); 
		
		// executing the statements
		dbHandler.createDataTablesByResource(ccs);
		
		create_iTexxUsers(connection);
		
	}

	
	
	
	public void create_iTexxUsers(Connection c) {
		DataBaseHandler dbh;
		Statement statement;
		String sql;
		String texxUser = "DROP USER IF EXISTS "+user.toUpperCase()+"; " +
		                  "CREATE USER "+user.toUpperCase()+" PASSWORD '"+password+"' ADMIN;";
		String texxRights = "GRANT SELECT,INSERT,UPDATE ON documents,folders,orphanes TO PUBLIC;";
						// SELECT,INSERT,UPDATE | ALL
		
		try {
		
			sql = "SELECT ID FROM INFORMATION_SCHEMA.USERS WHERE NAME ='RG';" ;
			statement = c.createStatement();
			ResultSet r = statement.executeQuery( sql );  
			c.commit() ;
			
			if ((r==null) || (r.wasNull())){
			
				statement = c.createStatement();
				statement.execute(texxUser);
				c.commit();
			}
			
			// SELECT * FROM INFORMATION_SCHEMA.RIGHTS
			// GRANTEE  	GRANTEETYPE  	GRANTEDROLE  	RIGHTS  	TABLE_SCHEMA  	TABLE_NAME  	ID  
			
			
			
			statement.close();
		
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

	private boolean checkStructure(Connection c) {
		
		ArrayList<String> tnames = new ArrayList<String>();
		String xmlstr;
		boolean rB=false;
		int expectedTableCount = 9999 ;
		
		Vector<Object> nodeObjs = null;
		XmlStringHandling xMsg = new XmlStringHandling();
		// read definition file 
		
		try {
			if (c!=null){
				 // open(c) ;
			}else {
				open();
			}
		
			jdbMetaData = c.getMetaData() ;
			if (metaData==null){
				dbHandler.updateMetaData(c);
				metaData = dbHandler.getMetadata() ; 
			}
			tnames = metaData.getTableNames(1) ;
			
			xmlstr = getConfigResource("create-db-sql-xml");
			xMsg.setContentRoot("randomgraph") ;
			
			nodeObjs = xMsg.getNodeList(xmlstr, "/randomgraph/database", "table");
			ArrayList<DataBaseCreateCommand> xcc = dbHandler.getCreateFromResource(xmlstr,"rg-fingerprints");
			
			if (nodeObjs!=null){
				expectedTableCount = nodeObjs.size() ;
				expectedTableCount = xcc.size() ; // 1 element per table
			}
				
			if (tnames.size() < expectedTableCount){
				// close();
				return rB;
			}
			
			for (int i=0;i<nodeObjs.size();i++){
				String tname = xMsg.getNodeInfo(nodeObjs.get(i), "/table", "name") ;
					// ((Node)nodeObjs).
				int p = tnames.indexOf(tname.toUpperCase()) ;
				if (p<0){
					rB=false;
					return rB;
				}
			}
			
			rB=true;
			
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		
		
		return rB;
	}


	
	public void close() {
		
		try{

			jdbMetaData = null;
			dbCatalog = "";
			disconnect();
			if (server != null){
				server.stop() ;
			}
			
		}catch(Exception e){
			
		}
		
	}

	protected void getJdbMeta(Connection c) throws Exception{
		
		jdbMetaData = c.getMetaData();
		dbCatalog = c.getCatalog() ;
		
	}

	public void setDbMetaData(DatabaseMetaData dbMetaData) {
		jdbMetaData = dbMetaData;
	}

	public void updateInfraStructure(Connection c ) throws Exception{

		getJdbMeta(c);
		
		if (dbHandler==null){
			out.printErr(1, "Datbase has not been properly instantiated.");
		}
		dbHandler.setiDb( iciDb ) ;

		// creates a new instance for new MetaData and retrieves the latest info again
		dbHandler.updateMetaData(c) ; 
	
	}
	
	
	synchronized public void open(Connection c) throws Exception{
		
		int result = -1;
		
		long now, startTime = System.currentTimeMillis() ;
		
		while ((result>= -4) && (result<0)){
			
			
			try{
				
				now = System.currentTimeMillis() ;
				if (now-startTime>10000){
					result = -5 ;	
				}
				iciDb = Db.open(c);
				// if it is locked by another user, try the next one, [...] then wait 
				// for the allowed period before raising the exception (-5)
				// By default, for each db we provide 10 users for concurrent access

				if (iciDb!=null){
					result = 0;
					break;
				}
				result = -3;
				
			}catch(Exception e){
			}	
			
		} // any user available
		if (c.isClosed()==false){
			updateInfraStructure(c);
		}
	}

	public void open() throws Exception{
		
		iciDb = Db.open(databaseUrl, user, password);
		connection = iciDb.getConnection();

		updateInfraStructure(connection);
	}

	  	
	public void startServer() {
		
		removeLock();
		try {
			prepareDatabase();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	/*
SELECT * FROM INFORMATION_SCHEMA.ROLES ;
SELECT * FROM INFORMATION_SCHEMA.RIGHTS ;
SELECT * FROM INFORMATION_SCHEMA.USERS ;
SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES ;
	 */
	public Connection getWireLessDatabaseServer(String user, String password){
		
		String url;
		
		Connection c = null;
		
		try {
			// "jdbc:h2:"+
			url = 	"jdbc:h2:tcp://localhost/~/"+databaseName ;
			
			Class h2Driver = Class.forName("org.h2.Driver");
	        
			c = DriverManager.getConnection( url, user, password);
			connection = c;
			
			dbHandler = new DataBaseHandler( this ) ;
			
		}catch(Exception e){
			e.printStackTrace() ;
		}
		return c;
	}
	
	public String createServer(String dbname, String filepath){
		

		
		// start the TCP Server
		String url;
		int result = -1;
		
		try {
			// "jdbc:h2:"+
			
			String h2Dir = DatabaseMgmt.setH2BaseDir(storageDir, _DB_TARGET_LOCATING); // DatabaseMgmt._BASEDIR_QUERY_STOR );
			String _db_dir = DFutils.createPath( h2Dir, "storage/") ; // storageDir;
			
			url = 	_db_dir+"/"+dbname+".h2.db"; // +";AUTO_SERVER=TRUE" ;
			url = url.replace("//", "/") ;

			if (DFutils.fileExists(url)==false){
				dbRecreated = true;
				connect(dbname, filepath);
				connection.close();
				connection = null ;
			}
			
			// this allows only for 1 server !!
			String[] args = new String[]{"-tcpAllowOthers","-tcpPort","8060"};
			try{
				server = Server.createTcpServer( args );
			}catch(Exception e){
				result = -4;
			}
			try{
				server.start() ;
			}catch(Exception e){
				result = -7;
			}
			// if creating or starting fails, we know that another server is already running
			if ((result == -4) || (result == -7)){
				connect(dbname, filepath);
				if (connection!=null){
					connection.close();	
				}else{
					result = -17; //!! CRITICAL !!
				}
			}
			int port = server.getPort();
			Service srvc = server.getService();
			boolean allowOthers = srvc.getAllowOthers() ;
			
			if (server.isRunning(true)){
				out.print(3, "H2 server for database <"+dbname+"> is running on port "+port) ;
			}
			 
			
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		//createTcpServer(args).start();

		// stop the TCP Server
		// server.stop();


		return "";
	}

	
	public String connect( String dbname, String filepath) throws FileNotFoundException{
		return connect( dbname, filepath, 0);
	}
	public String connect( String dbname, String filepath, int serverMode) throws FileNotFoundException{
		
		String dbfile ="";
		int err=0;
		
		
		
		try{
 			
			if ((connection!=null) && (connection.isClosed()==false)){
				dbfile = DFutils.createPath(filepath,dbname+".h2.db") ;
				if (DFutils.fileExists(dbfile)){
					return dbfile ;
				}
			}

			
			// String docoservUrl = connection.
			// "jdbc:h2:tcp://localhost/~/docoserv"
			// :nio
			String h2Dir = DatabaseMgmt.setH2BaseDir(storageDir, _DB_TARGET_LOCATING); // DatabaseMgmt._BASEDIR_QUERY_STOR );
			String _db_dir = DFutils.createPath( h2Dir, "storage/") ; // storageDir;

			if (_db_dir.endsWith("/")){
				_db_dir = _db_dir.substring(0,_db_dir.length()-1);
			}

			String url = 	"jdbc:h2:tcp://localhost"+_db_dir+"/"+dbname; // +";AUTO_SERVER=TRUE" ;
			serverMode = 1 ;

			if (serverMode>=1){
				url = 	"jdbc:h2:tcp://localhost/"+_db_dir+"/"+dbname;
			}
			
						
			databaseUrl = "";
				        /* 
				            ";MODE=MYSQL"
		    				";FILE_LOCK=FS" +
		    				";PAGE_SIZE=1024" +
		    				";CACHE_SIZE=8192";
						    DB_CLOSE_DELAY=10
				        */
		
			
	        
			if ((user==null) || (user.length()==0)){
				user="sa"; password="sa";
			}
			DbLogin login = new DbLogin(user,password) ; // may contain a pool of users
			DbConnector dbConnector = new DbConnector( (ConnectorClientIntf)this);

			connection = dbConnector.getConnection(url, login);
			

			dbfile = DFutils.createPath(filepath,dbname+".h2.db") ;
			
			File fil = new File(dbfile);
			if (fil.exists()){
				if (connection.isClosed()==false){
					databaseUrl = url;
				}
				
			}
			
			 
			// CALL DATABASE_PATH(); 
			 
			
			if (fil.exists()==false){
				throw new FileNotFoundException("\nDatabase file not found (err="+err+")\n"+
											    "expected directory : " + filepath+"\n"+
											    "expected db file   : " + dbname+".h2.db\n");
			}

			out.print(3,"database has been started ...");
			out.print(3,"...its connection url is : "+ databaseUrl) ;
			
		}catch(JdbcSQLException jx){
			System.out.println("Connecting to database <"+dbname+"> failed \n"+jx.getMessage() );
			String lockfile = filepath+"/"+dbname+".lock.db";
			File fil = new File(lockfile);
			fil.deleteOnExit();
			fil.delete();
			
			dbfile = "";
			
		}catch( SQLException sx){
			System.err.println ("Cannot connect to database server");
			dbfile = "";
			sx.printStackTrace();
			
		}catch( ClassNotFoundException e){
			dbfile = "";
			e.printStackTrace();
		}catch( Exception e){
			e.printStackTrace();
		} 
		
		 
		return dbfile;
	}


	public void checkForDbAvailability() { 
		
		try {
			
			if ((connection==null) || (connection.isClosed())){
				
				String h2Dir = DatabaseMgmt.setH2BaseDir(storageDir, _DB_TARGET_LOCATING); // DatabaseMgmt._BASEDIR_QUERY_STOR) ;
				h2Dir = DFutils.createPath(h2Dir, "storage/") ;

				connect( databaseName, h2Dir) ;
			}

			if (iciDb==null){
				if (connection.isClosed()==false){
					// connection.close();
				}
				
				open( connection );
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	public void disconnect(){
		
		try{
			
			if (connection != null){
			
				iciDb.close();
				
				if (connection.isClosed()==false){
				
					connection.commit();
					
					out.delay(100);
					
					connection.close(); 
				}
				
				out.delay(100);
				if (connection.isClosed()){
					connection = null;
				}
			}
			
		}catch( SQLException sx){
			sx.printStackTrace();
		}finally {
	         
	    }
		
	}

	public Connection getConnection(){
		
		return connection;
	}
	
	
	private String getStorageDir( ){
		/*
		String systemroot = rgProperties.getSystemRootDir();
		String prj = rgProperties.getPersistenceSettings().getProjectName() ;
		
		storageDir = DFutils.createPath(systemroot, prj+"/") ;
		storageDir = DFutils.createPath(storageDir, "storage/") ;
		*/
		
		
		String systemroot = rgProperties.getSystemRootDir();   // [path]/iTexx  
		
		String appcontext = rgProperties.getApplicationContext() ; 
		if (appcontext.contains("itexx")){  // itexx (as service within iTexx) or "standalone" (via applet)
			storageDir = DFutils.createPath(systemroot, "storage/") ;
			_DB_TARGET_LOCATING = 0;
		}else{
			// the storage is subject of the project
			String prj = rgProperties.getPersistenceSettings().getProjectName() ;
			storageDir = DFutils.createPath(systemroot, prj+"/") ;
			storageDir = DFutils.createPath(storageDir, "storage/") ;
			
			_DB_TARGET_LOCATING = 0; // DatabaseMgmt._BASEDIR_QUERY_PROJECT;
		}

		
		
		return storageDir ; 
	}
	
	private String getDBhomefile( String dbname){
		
		String filename="",filepath, user_home;
			
		getStorageDir();
		filepath = DFutils.createPath(storageDir, dbname);
		
		return filepath;
	}

	public RGraphPropertiesDefaultIntf getDocoProperties() {
		return rgProperties;
	}

	public String getDatabaseName() {
		return databaseName;
	}

	public String getDatabaseUrl() {
		return databaseUrl;
	}

	public Db getIciDb() {
		return iciDb;
	}

	public DatabaseMetaData getDbMetaData() {
		return jdbMetaData;
	}

	public String getDbCatalog() {
		return dbCatalog;
	}

	public DataBaseHandlerIntf getDbHandler() {
		return dbHandler;
	}

	public String getDatabaseFile() {
		return databaseFile;
	}

	public PrintLog getOut() {
		return out;
	}
	/** here we test variants of the most direct query ... removing pre- and suffixes, lower case etc.  */
	public ArrayList<Double> getFingerprintVariants(String wordLabel, String storLabel) throws Exception { 
		
		return null; 
	}
	
	public  RGraphFingerPrint getFingerprint(String wordLabel, String storLabel) throws Exception {
		
		ArrayList<Double> profile = new ArrayList<Double>();
		String clabel , fstr;
		Wordfinger f = new Wordfinger();
		List<Wordfinger> rf ;
		RGraphFingerPrint rgFP = new RGraphFingerPrint();
		
		
		open(connection);
		
		try{
		
			if (storLabel.length()==0){
				storLabel = RandomGraphIntf._FINGERPRINT_STORLABEL_EMPTY ;
			}
			clabel = wordLabel+storLabel;
			rf = iciDb.from(f).where(f.compoundlabel).is( clabel ).select();
			
			if ((rf!=null) && (rf.size()>0)){
				
				rgFP.rawlabel = rf.get(0).rawlabel;
				rgFP.storlabel = rf.get(0).storlabel;
				fstr = rf.get(0).fingerprint ;
				rgFP.fingerprint = fstr;
				rgFP.wordid      = rf.get(0).wordid ;
				
				if ((fstr.indexOf(";")>0) && (fstr.indexOf(";")<20)){
				// this is an indication whether the entry is a string of ;double; :: or an encoded object

					String[] svalues = fstr.split(";") ;
					for (int i=0;i<svalues.length;i++){
						double v = Double.parseDouble(svalues[i]) ;
						profile.add(v) ;
					}
					if (profile.size()>0){
						rgFP.profile.addAll(profile);
					}
					profile.clear();
				}else{
					
					Object obj = strObj.decode( fstr );
					
					if (obj != null){
						rgFP.profile = (ArrayList<Double>) obj;
					}	
				}
				
				
			} // any result ? ...if yes, we take only the first match
			
		}catch(Exception e){
			// null if result of query is empty 
		}
		
		
		return rgFP;
	}

	
	public long storeFingerprint( RGraphFingerPrint rgFP) {
		Long key=-1L,fingerprintId = -1L ;
		
		Wordfinger f = new Wordfinger();
		String fpstr,cLabel;
		ArrayList<Double> profile;

		cLabel = rgFP.compoundlabel ;
		
		try{
			

			f.compoundlabel = cLabel;
			if (rgProperties.isDatabaseProfileStoreAsObject()){
				fpstr = strObj.encode( rgFP.profile );	
			}else{
				fpstr =  rgFP.fingerprint ;
			}
			f.fingerprint = fpstr ;
			f.rawlabel = rgFP.rawlabel ;
			f.storlabel = rgFP.storlabel ;
			f.length    = rgFP.rawlabel.length() ;
			f.frequency = 1;
			f.guid      = rgFP.guid ;
			f.rawlabel  = rgFP.rawlabel ;
			
			
			key = iciDb.insertAndGetKey( f );
			
		}catch(Exception e){
			key = -7L;
		}
		
		/*
		iciDb.from(f).set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		             .set( f.rawlabel).to(rgFP.rawlabel)
		 */           
		fingerprintId = key;
		return fingerprintId ;
	}
	
	
	/**
	 * preparing and executing a bulk insertion
	 * 
	 * ensure single access in case of multi-threading
	 * 
	 * @param fingerprints
	 */
	synchronized public void storeFingerprints(ArrayList<RGraphFingerPrint> fingerprints) {
		
		String fpstr,cLabel;
		
		Wordfinger f = new Wordfinger();
		RGraphFingerPrint rgFP;
		
		QueryRunner query ;
		Object[][] sqlQueryParams = null ;
		
		
		// preparing the Object Array

		// params as of  Object[][] 
		
		sqlQueryParams = new Object[fingerprints.size()][13] ;
		                       
		for (int i=0;i<fingerprints.size();i++){
			
			rgFP = fingerprints.get(i) ;
			
			cLabel = rgFP.compoundlabel ;
			
			f.compoundlabel = cLabel;
			if (rgProperties.isDatabaseProfileStoreAsObject()){
				fpstr = strObj.encode( rgFP.profile );	
			}else{
				fpstr =  rgFP.fingerprint ;
			}
			f.fingerprint = fpstr ;
			f.rawlabel 	  = rgFP.rawlabel ;
			f.storlabel   = rgFP.storlabel ;
			f.length      = rgFP.rawlabel.length() ;
			f.frequency   = 1; // FIXME put real frequency of word here
			f.guid        = rgFP.guid ;

			 
			// we should replace auto-increment, which would need a further entr in the object array row
			// sqlQueryParams[i][0] = actualDbId ;  // = wordid
			sqlQueryParams[i][0] = f.guid;
			sqlQueryParams[i][1] = f.rawlabel;
			sqlQueryParams[i][2] = f.storlabel;
			sqlQueryParams[i][3] = f.compoundlabel; 
			sqlQueryParams[i][4] = f.length;
			sqlQueryParams[i][5] = f.frequency;
			
			
			// RELFREQ, NGRAM3, HASHKEY1,HASHKEY2,HASHKEY3,USEDHASH
			
			sqlQueryParams[i][6] = 0.0; // relfreq
			sqlQueryParams[i][7] = "";  // ngram3;
			sqlQueryParams[i][8] = 0L;
			sqlQueryParams[i][9] = 0L;
			sqlQueryParams[i][10] = 0L;
			sqlQueryParams[i][11] = -1;
			
			sqlQueryParams[i][12] = f.fingerprint;

		} // i->            wordid  || guid,rawlabel,storlabel,compoundlabel,length,frequency,fingerprint
		
		// params = listOfObjectArrays.toArray(params);

		try {
			
			query = new QueryRunner();
			
			/*
			 * new Object[] { str1, str2, str3});
			 * qr.update("insert into MyTable (param1,param2,param3) values (?,?,?)",
			 */
			if ((sqlQueryParams !=null) && (sqlQueryParams.length>0)){
				
				int[] ixes = query.batch( iciDb.getConnection(), 
								  			   "insert into wordfinger "+
											   "        (guid,rawlabel,storlabel,compoundlabel,length,frequency,relfreq,ngram3,hashkey1,hashkey2,hashkey3,usedhash,fingerprint) " +
											   "        values (?,?,?,?,?,?,?,?,?,?,?,?,?)", sqlQueryParams );
			}
			        

		} catch (SQLException e) {
			
			e.printStackTrace();
		}
	}

	public long updateFingerprint(String wordLabel, String storLabel) {
		Long fingerprintId = -1L ;
		Wordfinger f = new Wordfinger();
		
		
		
		
		return fingerprintId ;
	}

	
	 
	
}











